package com.tabjin.rfidsocket.service.sys.impl;

import com.google.common.collect.Lists;
import com.tabjin.rfidsocket.authority.shiro.JwtUtil;
import com.tabjin.rfidsocket.authority.shiro.ThreadLocalToken;
import com.tabjin.rfidsocket.common.RequestHolder;
import com.tabjin.rfidsocket.mapper.mapper.*;
import com.tabjin.rfidsocket.pojo.SysUser;
import com.tabjin.rfidsocket.pojo.vo.SysAclVO;
import com.tabjin.rfidsocket.pojo.vo.SysUserVO;
import com.tabjin.rfidsocket.service.sys.SysCoreService;
import com.tabjin.rfidsocket.service.sys.SysUserService;
import org.apache.commons.collections4.CollectionUtils;
import org.springframework.stereotype.Service;

import java.util.List;
import java.util.Set;
import java.util.stream.Collectors;

/**
 * @author tabjin
 * create at 2020/12/24 16:53
 * @program rfid_socket
 * @description
 */
@Service
public class SysCoreServiceImpl implements SysCoreService {

    private final SysAclMapper sysAclMapper;
    private final SysAclCustomMapper sysAclCustomMapper;
    private final SysRoleUserMapper sysRoleUserMapper;
    private final SysRoleUserCustomMapper sysRoleUserCustomMapper;
    private final SysRoleAclMapper sysRoleAclMapper;
    private final SysAclModuleCustomMapper sysAclModuleCustomMapper;
    private final SysRoleAclCustomMapper sysRoleAclCustomMapper;
    private final SysUserService sysUserService;
    private final ThreadLocalToken threadLocalToken;
    private final JwtUtil jwtUtil;

    public SysCoreServiceImpl(SysAclMapper sysAclMapper, SysAclCustomMapper sysAclCustomMapper, SysRoleUserMapper sysRoleUserMapper, SysRoleUserCustomMapper sysRoleUserCustomMapper, SysRoleAclMapper sysRoleAclMapper, SysAclModuleCustomMapper sysAclModuleCustomMapper, SysRoleAclCustomMapper sysRoleAclCustomMapper, SysUserService sysUserService, ThreadLocalToken threadLocalToken, JwtUtil jwtUtil) {
        this.sysAclMapper = sysAclMapper;
        this.sysAclCustomMapper = sysAclCustomMapper;
        this.sysRoleUserMapper = sysRoleUserMapper;
        this.sysRoleUserCustomMapper = sysRoleUserCustomMapper;
        this.sysRoleAclMapper = sysRoleAclMapper;
        this.sysAclModuleCustomMapper = sysAclModuleCustomMapper;
        this.sysRoleAclCustomMapper = sysRoleAclCustomMapper;
        this.sysUserService = sysUserService;
        this.threadLocalToken = threadLocalToken;
        this.jwtUtil = jwtUtil;
    }

    /**
     * 获取用户的权限点列表
     *
     * @return
     */
    @Override
    public List<SysAclVO> getCurrentUserAclList() {
        // 获取到当前用户的id
        // TODO 空指针异常
        Integer userId = RequestHolder.getCurrentUser().getId();
//        Integer userId = jwtUtil.getUserId(threadLocalToken.getToken());
        // 用户权限点列表
        return getUserAclList(userId);
    }

    /**
     * 获取用户已分配的所有权限点
     *
     * @param userId
     * @return
     */
    @Override
    public List<SysAclVO> getUserAclList(Integer userId) {
        if (isSuperAdministrator()) {
            // 超级管理员取出所有权限点数据
            return sysAclCustomMapper.getAll();
//            return sysAclCustomMapper.selectAll();
        }

        // 取出用户已分配的角色id
//        Example example = new Example(SysRoleUser.class);
//        Example.Criteria criteria = example.createCriteria();
//        criteria.andEqualTo("userId", userId);
//        List<SysRoleUser> sysRoleUserList = sysRoleUserMapper.selectByExample(example);// 根据userId查询角色列表
//        =List<Integer> userRoleIdList  Lists.newArrayList();
//        for (SysRoleUser sysRoleUser : sysRoleUserList) {
//            userRoleIdList.add(sysRoleUser.getRoleId());
//        }
        // 角色用户关联查询
        List<Integer> userRoleIdList = sysRoleUserCustomMapper.getRoleIdListByUserId(userId);
        if (CollectionUtils.isEmpty(userRoleIdList)) {
            return Lists.newArrayList();// 无值返回空的列表
        }

        // 已分配角色需要并集
        // 根据角色id列表查询权限点id列表
        List<Integer> userAclIdList = sysRoleAclCustomMapper.getAclIdListByRoleIdList(userRoleIdList);
        if (CollectionUtils.isEmpty(userAclIdList)) {
            return Lists.newArrayList();// 此时角色的权限点无
        }

        // id -> object
        // 用户权限点列表
        List<SysAclVO> sysAclVOList = sysAclCustomMapper.getByIdList(userAclIdList);
        return sysAclVOList;
    }

    /**
     * 获取角色的已分配的权限点列表
     *
     * @param roleId 角色id
     * @return
     */
    @Override
    public List<SysAclVO> getCurrentRoleAclList(Integer roleId) {
        List<Integer> aclIdList = sysRoleAclCustomMapper.getAclIdListByRoleIdList(Lists.newArrayList(roleId));
        if (CollectionUtils.isEmpty(aclIdList)) {
            return Lists.newArrayList();
        }
        return sysAclCustomMapper.getByIdList(aclIdList);
    }

    /**
     * 核心：判断用户是否有访问url的权限
     *2
     * @param url
     * @return boolean
     */
    @Override
    public boolean hasUrlAcl(String url) {
        // 超级管理员可以访问一切
        if (isSuperAdministrator()) {
            // TODO
            return true;
        }
        List<SysAclVO> sysAclList = sysAclCustomMapper.getByUrl(url);
        if (CollectionUtils.isEmpty(sysAclList)) {
            return true;// 权限点根本没有url，所以自然不需要权限校验，直接可以访问
        }
        // 判断用户是否拥有选出的权限点
        // 例如/sys/user/action.json有多个
        List<SysAclVO> currentUserAclList = getCurrentUserAclList();// 待校验的权限点列表
        Set<Integer> userAclIdSet = currentUserAclList.stream().map(acl -> acl.getId()).collect(Collectors.toSet());
        boolean hasValidAcl = false;// 是否有效权限点
        // 规则：只要有一个权限点有权限，则认为有权限访问
        for (SysAclVO sysAcl : sysAclList) {
            // 判断一个用户时候具有访问某个权限点的访问权限
            if (sysAcl == null || sysAcl.getStatus() != 1) {// 权限不需要校验
                continue;// 实际上相当于权限点无效，继续接下来的校验
            }
            // 权限点有效，才进行权限关联关系的判断，则取出当前用户已有的权限点
            hasValidAcl = true;
            if (userAclIdSet.contains(sysAcl.getId())) {// 用户拥有的权限id包含当前权限id，具有访问权限
                return true;
            }
        }
        if (!hasValidAcl) {// 没有可校验的权限点
            return true;
        }

        return false;
    }

    /**
     * 校验超级管理员
     *
     * @return
     */
    public boolean isSuperAdministrator() {
        // 这里是我自己定义了一个假的超级管理员规则，实际中要根据项目进行修改
        // 可以是配置文件获取，可以指定某个用户，也可以指定某个角色
        // TODO
        SysUserVO sysUserVO = RequestHolder.getCurrentUser();
        Integer userId = jwtUtil.getUserId(threadLocalToken.getToken());
        SysUser sysUser = sysUserService.searchUserById(userId);
        if (sysUser.getMail().contains("admin")) {
            return true;
        }
        return false;
    }
}
